﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Sintaxe.Simbolos;

/*
 * Classe  do qual todos os tipos de instruções devem herdar.
 * Os campos sõ o conjunto mínimo para o montador
 * Cada processador deveria definir uma classe derivada, representando as instruções da maquina dele.
 */

namespace Sintaxe.Instrucoes
{
    public class Instrucao 
    {
        // atributos persistentes
        public char CatI; // Categoria da instruçaõ. Instrução vazia
        public string Operacao;
        public string Rotulo;
        public string Imediato;
        public string Comentario;

        // atributos temporários da montagem vertical
        public UInt64 edcAtual;   // endereço átual da instrução
        public UInt64 edcProx;    // endereço do proximo item na memória

        // atributos temporários da montagem horizontal
        public SimboloFonte DefSim;  // em caso a instrução definir rótulo, ...
        public FixupFonte Fixup;     // em caso utilizar endereço relocavel
        public PropFonte Prop;       // para diretivas liderando com propriedades gerais de programa
        public byte[] RepBin;        // resultado da montagem na memória


        // Constructor
        //
        public Instrucao()
        {
            CatI = ' '; // Instrução vazia
        }


        public Instrucao(BinaryReader brFonte)
        {
            // Criar objeto baseado em um arquivo
            CatI = brFonte.ReadChar();
            Operacao = brFonte.ReadString();
            Rotulo = brFonte.ReadString();
            Imediato = brFonte.ReadString();
            Comentario = brFonte.ReadString();
        }
        public virtual void Gravar(BinaryWriter bwFonte)
        {
            // gravar objeto para arquivo
            // classes derivadas devem invocar isso e depois gravar argumentos próprios delas
            bwFonte.Write(CatI);
            bwFonte.Write(Operacao);
            bwFonte.Write(Rotulo);
            bwFonte.Write(Imediato);
            bwFonte.Write(Comentario);
            //bwFonte.Write(edcAtual);  estes atributos pertencem à uma montagem, não ao programa
            //bwFonte.Write(edcProx);
        }

        public virtual string RepVisual(IProcessador prmProc)
        {
            // Derivar a representacao visual da instrucao dos atributos e do formato
            string ss;

            ss = RepVisuGeral();           // caso geral?
            if (ss != null) return ss;

            ss = RepVisuDire();            // uma diretiva?
            if (ss != null) return ss;

            return RepVisuMnemo(prmProc);   // se não, caso específico

        }
        protected String RepVisuGeral()
        {
            // produzir a representação visual em todos os casos quais não envolvem formatação específica da operação
            // devolver null em caso esteja específica
            // jogar exceção em caso de probleminhas
            // Derivar a representacao visual da instrucao dos atributos e do formato

            string strCom = "";

            // salao de beleza para o comentário
            if ((Comentario != null) & (Comentario != ""))
            {
                if (Comentario.Substring(0, 2) != "//") strCom = "//";
                strCom += Comentario;
            }

            // selecionar algoritmo específico ou retornar string simples
            switch (CatI)
            {
                case 'C':    // Instrucao da maquina ou diretiva
                case 'c':
                    if ((Operacao == null) || (Operacao == "")) return ("Erro. Categoria 'C' incompativel com operação vazia.");
                    return null;     // criar representacao baseada em algoritmo específico mais tarde

                case ' ':    // Linha vazia
                    return "";

                case '/':    // Linha inteira de comentário
                    return strCom;

                default:
                    return String.Format("Erro. Categoria '{0}' é ilegal para uma instrução.", CatI);

            }
        }
        protected String RepVisuDire()
        {
            // A representação depende da operação, porque Cat == 'C' e operacao != "".
            // Na classe base, a representação derivada das diretivas pode ser construida.
            // Para instruções da maquina,
            // a classe da instrução dele deve ser derivada da Instrução e este método overridden.
            // O parâmetro está utilizado somente nos métodos das classes derivadas.

            string ss;
            string RotLin;
            string strCom = "";
            string FmtAtual = "";

            // temos um rótulo?
            if ((Rotulo == null) | (Rotulo == "")) RotLin = "          "; // 10 espacos
            else RotLin = String.Format("{0, -8} :", Rotulo);

            // salao de beleza para o comentário
            if ((Comentario != null) & (Comentario != ""))
            {
                if (Comentario.Substring(0, 2) != "//") strCom = "//";
                strCom += Comentario;
            }

            // criar representacao baseado em Format
            Diretiva dd = (Diretiva)Diretor.Diretivas.Item(Operacao);

            if (dd == null) return null;

            FmtAtual = dd.fmtVisu;
            ss = String.Format(FmtAtual, RotLin,
                                         Operacao,
                                         Imediato,
                                         strCom);
            return ss;
        }
        protected virtual string RepVisuMnemo(IProcessador prmProc)
        {
            return String.Format("Erro. {0} não é diretiva e a classe Instrução não lida com instruções da maquina", Operacao);
        }

    }
}
